﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using WebLayer;
using WebPageParser;
using TableParsers;

namespace Speakup
{
    static public class ClassListFetcher
    {
        static public ClassList Fetch(LoginToken loginToken)
        {
            WebPageCache cache = new WebPageCache();
            cache.LoginToken = loginToken;
            return Fetch(cache);
        }
        static public ClassList Fetch(WebPageCache cache)
        {
            /* Get Past history: attended, missed, cancelled classes*/
            ClassList pastList = FetchPastClasses(cache);
            ClassList futureList = FetchFutureClasses(cache);

            pastList.Merge(futureList);
            return pastList;
        }

        static public ClassList FetchPastClasses(LoginToken loginToken)
        {
            WebPageCache cache = new WebPageCache();
            cache.LoginToken = loginToken;
            return FetchPastClasses(cache);
        }
        static public ClassList FetchPastClasses(WebPageCache cache)
        {
            ClassList list = new ClassList();

            AttendanceTable attendanceTable = AttendanceTable.Fetch(cache);
            CancelledTable cancelledTable = CancelledTable.Fetch(cache);

            foreach (AttendanceTableEntry entry in attendanceTable)
            {
                Class speakupClass = new Class();
                speakupClass.ClassId = 0;

                if (entry.Present)
                {
                    speakupClass.ClassStatus = ClassStatus.Attended;
                }
                else
                {
                    speakupClass.ClassStatus = ClassStatus.Missed;
                }

                speakupClass.ClassName = entry.ClassName;
                speakupClass.ClassTime = entry.ClassTime;
                list.Add(speakupClass);
            }

            foreach (CancelledTableEntry entry in cancelledTable)
            {
                Class speakupClass = new Class();
                speakupClass.ClassId = 0;
                speakupClass.ClassName = entry.ClassName;
                speakupClass.ClassTime = entry.ClassTime;
                speakupClass.ClassStatus = ClassStatus.Cancelled;

                list.Add(speakupClass);
            }

            return list;
        }

        static public ClassList FetchFutureClasses(LoginToken loginToken)
        {
            WebPageCache cache = new WebPageCache();
            cache.LoginToken = loginToken;
            return FetchFutureClasses(cache);
        }
        static public ClassList FetchFutureClasses(WebPageCache cache)
        {
            ClassList list = new ClassList();
            List<int> ids = new List<int>();

            BookTable bookTable = BookTable.Fetch(cache);
            ReservedTable reservedTable = ReservedTable.Fetch(cache);
            WaitListTable waitListTable = WaitListTable.Fetch(cache);

            foreach (ReservedTableEntry entry in reservedTable)
            {
                Class speakupClass = new Class();
                speakupClass.ClassId = entry.ClassId;
                speakupClass.ClassName = entry.ClassName;
                speakupClass.ClassTime = entry.ClassTime;
                speakupClass.ClassStatus = ClassStatus.Reserved;

                ids.Add(entry.ClassId);
                list.Add(speakupClass);
            }

            foreach (WaitListTableEntry entry in waitListTable)
            {
                Class speakupClass = new ReservedWaitListClass();
                speakupClass.ClassId = entry.ClassId;
                speakupClass.ClassName = entry.ClassName;
                speakupClass.ClassTime = entry.ClassTime;
                speakupClass.ClassStatus = ClassStatus.ReservedWaitList;

                ids.Add(entry.ClassId);
                list.Add(speakupClass);
            }

            /* zero ids should be ignored */
            if (ids.Contains(0))
            {
                ids.Remove(0);
            }

            foreach (BookTableEntry entry in bookTable)
            {
                // if such an id is already used, we 
                // do not list this class any more
                if (ids.Contains(entry.ClassId))
                {
                    continue;
                }

                Class speakupClass;

                if (entry.Available)
                {
                    speakupClass = new Class();
                    speakupClass.ClassStatus = ClassStatus.Available;
                }
                else
                {
                    speakupClass = new AvailableWaitListClass();
                    speakupClass.ClassStatus = ClassStatus.AvailableWaitList;
                }

                speakupClass.ClassId = entry.ClassId;
                speakupClass.ClassName = entry.ClassName;
                speakupClass.ClassTime = entry.ClassTime;

                list.Add(speakupClass);
            }

            FixFututreClassesList(list);
            return list;
        }

        static void FixFututreClassesList(ClassList list)
        {
            IEnumerable<ReservedWaitListClass> reservedWaitListClasses = null;
            reservedWaitListClasses = from c in list
                                      where c.ClassStatus == ClassStatus.ReservedWaitList
                                      select c as ReservedWaitListClass;

            /* find and connect alternative wait classes */
            foreach (ReservedWaitListClass reserved in reservedWaitListClasses)
            {
                IEnumerable<AvailableWaitListClass> availableWaitListClasses = null;
                availableWaitListClasses = from c in list
                                           where c.ClassStatus == ClassStatus.AvailableWaitList &&
                                                 c.ClassTime.Date == reserved.ClassTime.Date &&
                                                 c.ClassName.Kind == reserved.ClassName.Kind &&
                                                 c.ClassName.Type == reserved.ClassName.Type &&
                                                 c.ClassName.Name == reserved.ClassName.Name
                                           select c as AvailableWaitListClass;

                foreach (AvailableWaitListClass available in availableWaitListClasses)
                {
                    available.ProbableWaiters.Add(reserved);
                    reserved.ProbableReservations.Add(available);
                }
            }

            /* if there is a definite available winner, use it */
            foreach (ReservedWaitListClass reserved in reservedWaitListClasses.ToList())
            {
                if (reserved.ProbableReservations.Count == 1)
                {
                    Class available = reserved.ProbableReservations[0];
                    reserved.ClassId = available.ClassId;
                    reserved.ClassTime = available.ClassTime;
                    list.Remove(available);
                }
            }

        }
    }
}
